package org.domus.sipnmove;

import gov.nist.javax.sip.stack.SIPServerTransaction;

import java.text.ParseException;
import java.util.Properties;

import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.PeerUnavailableException;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.header.ContactHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ToHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

/**
 * Typical UserAgent that initiates a Socket communication after SIP Session
 * initialized
 */
public class UserAgentImpl implements SipListener {

	/**
	 * Sip factory dependency
	 */
	private SipFactory sipFactory;

	private SipStack sipStack;

	/**
	 * Listening port of the UserAgent
	 */
	private Integer port;

	private MessageFactory messageFactory;

	private AddressFactory addressFactory;

	private HeaderFactory headerFactory;

	private String ipAddress;

	/**
	 * Entry point of the UserAgent process
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

		if (args.length < 2) {
			System.out.println("Pas assez d'arguments");
			System.exit(-1);
		}

		SipFactory sipFactory = SipFactory.getInstance();

		new UserAgentImpl(args[0], new Integer(args[1]), sipFactory);
	}

	/**
	 * Constructor of the UserAgent
	 */
	public UserAgentImpl(String ipAddress, Integer port, SipFactory sipFactory) {
		this.ipAddress = ipAddress;
		this.port = port;
		this.sipFactory = sipFactory;
		init();
	}

	/**
	 * Initialize the UserAgent
	 */
	private void init() {
		sipFactory.setPathName("gov.nist");

		// The port number is used as a Unique ID
		Properties properties = new Properties();
		properties.setProperty("javax.sip.STACK_NAME", "useragent" + port);
		// You need 16 for logging traces. 32 for debug + traces.
		// Your code will limp at 32 but it is best for debugging.
		properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
		properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "ua" + port + "_debug.txt");
		properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "ua" + port + "_log.txt");

		try {
			// Create SipStack object
			sipStack = sipFactory.createSipStack(properties);
		} catch (PeerUnavailableException e) {
			// could not find
			// gov.nist.jain.protocol.ip.sip.SipStackImpl
			// in the classpath
			e.printStackTrace();
			System.err.println(e.getMessage());
			if (e.getCause() != null)
				e.getCause().printStackTrace();
			System.exit(0);
		}

		try {
			headerFactory = sipFactory.createHeaderFactory();
			addressFactory = sipFactory.createAddressFactory();
			messageFactory = sipFactory.createMessageFactory();

			ListeningPoint listeningPoint = sipStack.createListeningPoint(ipAddress, port, "udp");

			SipProvider sipProvider = sipStack.createSipProvider(listeningPoint);
			sipProvider.addSipListener(this);

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	/** SIP methods processing **/

	private void processCancel(RequestEvent requestEvent) {
		// TODO Auto-generated method stub

	}

	private void processBye(RequestEvent requestEvent) {
		// TODO Auto-generated method stub

	}

	private void processAck(RequestEvent requestEvent) {

		initSession();
	}

	private void initSession() {
		// QuickSocket.openSocket(ipAddress, port);
	}

	/**
	 * Process INVITE method from SIP protocol
	 * 
	 * @param requestEvent
	 */
	private void processInvite(RequestEvent requestEvent) {
		SipProvider sipProvider = (SipProvider) requestEvent.getSource();
		Request request = requestEvent.getRequest();

		try {

			// Rings while user as not responded

			Response ringResponse = messageFactory.createResponse(Response.RINGING, request);
			ServerTransaction serverTransaction = requestEvent.getServerTransaction();

			if (serverTransaction == null) {
				serverTransaction = sipProvider.getNewServerTransaction(request);
			}

			serverTransaction.sendResponse(ringResponse);

			// Send OK when the user hangs off the combinééééé

			Response okResponse = messageFactory.createResponse(Response.OK, request);
			Address address = addressFactory.createAddress("<sip:" + ipAddress + ":" + port + ">");
			ContactHeader contactHeader = headerFactory.createContactHeader(address);
			ToHeader toHeader = (ToHeader) okResponse.getHeader(ToHeader.NAME);

			toHeader.setTag("4321"); // Application is supposed to set.
			okResponse.addHeader(contactHeader);

			serverTransaction.sendResponse(okResponse);

		} catch (Exception ex) {
			ex.printStackTrace();
			System.exit(0);
		}

	}

	/** Methods for SipListener behavior **/

	@Override
	public void processDialogTerminated(DialogTerminatedEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void processIOException(IOExceptionEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void processRequest(RequestEvent requestEvent) {
		Request request = requestEvent.getRequest();
		ServerTransaction serverTransaction = requestEvent.getServerTransaction();

		System.out.println("\n\nRequest " + request.getMethod() + " received at " + sipStack.getStackName() + " with server transaction id " + serverTransaction);

		if (request.getMethod().equals(Request.INVITE)) {
			processInvite(requestEvent);
		} else if (request.getMethod().equals(Request.ACK)) {
			processAck(requestEvent);
		} else if (request.getMethod().equals(Request.BYE)) {
			processBye(requestEvent);
		} else if (request.getMethod().equals(Request.CANCEL)) {
			processCancel(requestEvent);
		} else {

			// TODO : Améliorer ici
			try {
				serverTransaction.sendResponse(messageFactory.createResponse(202, request));

				// send one back
				SipProvider prov = (SipProvider) requestEvent.getSource();
				Request refer = requestEvent.getDialog().createRequest("REFER");
				requestEvent.getDialog().sendRequest(prov.getNewClientTransaction(refer));

			} catch (SipException e) {
				e.printStackTrace();
			} catch (InvalidArgumentException e) {
				e.printStackTrace();
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void processResponse(ResponseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void processTimeout(TimeoutEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void processTransactionTerminated(TransactionTerminatedEvent arg0) {
		// TODO Auto-generated method stub

	}

	/** Setters and Getters **/

	public SipFactory getSipFactory() {
		return sipFactory;
	}

	public void setSipFactory(SipFactory sipFactory) {
		this.sipFactory = sipFactory;
	}

}
